﻿
using System.IO;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSQLObjects.Filter;
using NSQLObjects.Model;
using NSQLObjects.Persistence;
using NSQLObjectsTests.DBModel;

namespace NSQLObjectsTests
{
    [TestClass]
    public class ProductTests
    {
        [ClassInitialize()]
        public static void ProductTestsInitialize(TestContext testContext) {
            DefaultSetup.Connect();
            using (var ctx = SqlDataContext.CreateContext()) {
                ProductCount pc = new ProductCount();
                pc.Load(ctx);
                while (pc.Count < TestProductCount) {
                    pc.Count++;
                    CreateProduct(ctx, pc.Count);
                }
                ctx.SubmitChanges();
            }
        }

        private static void CreateProduct(SqlDataContext ctx, int productNro) {
            ProductRow pr = new ProductRow() { ID = productNro, ProductName = "Product " + productNro.ToString(), UnitPrice = 1.0m, Version = 0 };
            ctx.Insert(pr);
        }

        [TestMethod()]
        public void CountRowTest() {
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.PageSize = 3;
                ap.LoadPage(ctx);
                Assert.AreEqual(3, ap.Count);                
                Assert.AreEqual(TestProductCount, ap.TotalRows);
            }
        }

        [TestMethod()]
        public void ValidateRowTest() {
            long id = 2;
            Product product = null;
            long version;
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.IsEQ(p => p.ID, id);
                ap.Load(ctx);
                Assert.AreEqual(1, ap.Count);
                product = ap[0];
                version = product.Version;
                ap[0].UnitPrice += 1.0m;
                ctx.SubmitChanges();
            }
            Assert.AreEqual(version + 1, product.Version);
        }

        [TestMethod()]
        public void FilterTest1() {
            decimal sum1 = 0m;
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.IsGE(p => p.ID, 10);
                ap.IsLT(p => p.ID, 20);
                ap.Load(ctx);
                Assert.AreEqual(10, ap.Count);
                decimal price1 = 10.0m;
                foreach (var p in ap) {
                    p.UnitPrice = price1;
                    sum1 += price1;
                    price1 += 0.5m;                    
                }
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.IsGT(p => p.ID, 9);
                ap.IsLE(p => p.ID, 19);
                ap.Load(ctx);
                Assert.AreEqual(10, ap.Count);
                Assert.AreEqual(sum1, ap.Sum(p => p.UnitPrice));

                // Count with server sum function
                ProductSums ps = new ProductSums() { MinID = 10, MaxID = 19 };
                ps.Load(ctx);
                Assert.IsTrue(ps.IsLoaded);
                Assert.AreEqual(sum1, ps.UnitPriceSum);
            }

            using (var ctx = SqlDataContext.CreateContext())
            {
                AllProducts ap = new AllProducts();
                ap.Has(p => p.ID >= 10).Has(p => p.ID <= 19);
                ap.Load(ctx);
                Assert.AreEqual(10, ap.Count);
                Assert.AreEqual(sum1, ap.Sum(p => p.UnitPrice));

                // Count with server sum function
                ProductSums ps = new ProductSums() { MinID = 10, MaxID = 19 };
                ps.Load(ctx);
                Assert.IsTrue(ps.IsLoaded);
                Assert.AreEqual(sum1, ps.UnitPriceSum);
            }
        }

        [TestMethod()]
        public void LikeFilterTest() {
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.IsLike(p => p.ProductName, "Product 1%");
                ap.IsNE(p => p.ID, 100);
                ap.Load(ctx);
                Assert.AreEqual(11, ap.Count);  // 1, 10-19
            }
        }

        [TestMethod()]
        [ExpectedException(typeof(NSQLObjects.Exceptions.InvalidDataException))]
        public void InvalidFetchTest()
        {
            using (var ctx = SqlDataContext.CreateContext())
            {
                InvalidFetch iff = new InvalidFetch();
                iff.Load(ctx);
            }
        }

        [TestMethod()]
        [ExpectedException(typeof(NSQLObjects.Exceptions.InvalidDataException))]
        public void SingletonQueryTest1() {
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                // Fail beacause too many rows in returned
                var row = ap.LoadSingle(ctx, true);
            }
        }

        [TestMethod()]
        [ExpectedException(typeof(NSQLObjects.Exceptions.InvalidDataException))]
        public void SingletonQueryTest2() {
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.IsNull(r => r.ID);
                // Fail beacause no rows in returned
                var row = ap.LoadSingle(ctx, true);
            }
        }

        [TestMethod()]
        public void RangeFilterTest()
        {
            byte[] testImage = LoadImage();
            using (var ctx = SqlDataContext.CreateContext())
            {
                ProductPicture pp = new ProductPicture();
                pp.InRange(row => row.ID, 30, 40);
                Assert.AreEqual(1, pp.Filters.Count());
                pp.Load(ctx);
                Assert.AreEqual(11, pp.Count);
                int count = 0;
                foreach (var row in pp)
                {
                    if (count % 2 == 0)
                        row.ProductPicture = testImage;
                    else
                        row.ProductPicture = null;
                    count++;
                }
                ctx.SubmitChanges();
            }
            using (var ctx = SqlDataContext.CreateContext())
            {
                // Test is null filter
                ProductPicture pp = new ProductPicture();
                pp.InRange(row => row.ID, 30, 40).IsNull(row => row.ProductPicture);
                pp.Load(ctx);
                Assert.AreEqual(5, pp.Count);
                // Test is not null filter
                pp = new ProductPicture();
                pp.InRange(row => row.ID, 30, 40).IsNotNull(row => row.ProductPicture);
                pp.Load(ctx);
                Assert.AreEqual(6, pp.Count);

                // Test clear filters
                pp = new ProductPicture();
                pp.InRange(row => row.ID, 30, 32);
                pp.ClearFilters();
                pp.InRange(row => row.ID, 30, 33);
                pp.Load(ctx);
                Assert.AreEqual(4, pp.Count);
            }
        }

        [TestMethod]
        [ExpectedException(typeof(NSQLObjects.Exceptions.MetadataException))]
        public void InvalidFilterTest() {
            using (var ctx = SqlDataContext.CreateContext()) {
                AllProducts ap = new AllProducts();
                ap.Has(p => (p.ID + 1) >= 10);
                ap.Load(ctx);
            }

        }

        private byte[] LoadImage()
        {
            using (var s = typeof(ProductTests).Assembly.GetManifestResourceStream("NSQLObjects.Tests.Resources.TestImage.jpg"))
            {
                MemoryStream ms = new MemoryStream();
                int len;
                byte []content = new byte[10000];
                while ((len = s.Read(content, 0, 10000)) != 0)
                {
                    ms.Write(content, 0, len);
                }
                return ms.ToArray();
            }
        }

        internal const int TestProductCount = 100;
    }
}

namespace NSQLObjectsTests.DBModel
{
    partial class Product: ITableRowValidate
    {
        
        #region ITableRowValidate Members

        public void ValidateRow(RowStates state, ITableRow originalValues, ICommitContext context) {
            // Automatically increment version
            if (state != RowStates.New)
                Version++;
        }

        #endregion
    }


}